#include "pch.hpp"
#include "timer.hpp"

using boostext::programming_error;




Timer::Timer()
	: m_startTick(0)
	, m_startTime(0)
	, m_lastTime(0)
	, m_frequency(0)
{
	reset();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Timer::~Timer()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Timer::reset()
{
	// Find out the lowest core this process is running on
	DWORD_PTR procMask;
	DWORD_PTR sysMask;
	GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);

	procMask = 1;
	DWORD_PTR timerMask = 1;
	while((timerMask & procMask ) == 0)
		timerMask <<= 1;

	HANDLE thread = GetCurrentThread();

	// Set affinity to the first core
	DWORD_PTR oldMask = SetThreadAffinityMask(thread, timerMask);

	// Get the constant frequency
	LARGE_INTEGER frequency;
	QueryPerformanceFrequency(&frequency);
	BOOST_STATIC_ASSERT(sizeof(m_frequency) == sizeof(frequency.QuadPart));
	m_frequency = frequency.QuadPart;

	// Query the timer
	LARGE_INTEGER startTime;
	QueryPerformanceCounter(&startTime);
	m_startTime = startTime.QuadPart;
	m_startTick = GetTickCount();

	// Reset affinity
	SetThreadAffinityMask(thread, oldMask);

	m_lastTime = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




int64 Timer::elapsed()
{
	LARGE_INTEGER curTime;
	QueryPerformanceCounter(&curTime);

	int64 newTime = curTime.QuadPart - m_startTime;
	int64 newTicks = 1000 * newTime / m_frequency;

	// Detect and compensate for performance counter leaps
	// (surprisingly common, see Microsoft KB: Q274323)
	// In addition, prevent our own counter from errors when
	// the magical 47days limit has been reached.
	uint32 newTick = GetTickCount();
	uint32 check = 0;
	if(newTick > m_startTick) check = newTick - m_startTick;
	else                      check = newTick + (std::numeric_limits<uint32>::max() - m_startTick);

	int32 msecOff = static_cast<int32>(newTicks - check);
	if(msecOff < -100 || msecOff > 100)
	{
		// We must keep the timer running forward :)
		int64 adjust = std::min(msecOff * m_frequency / 1000, newTime - m_lastTime);
		m_startTime += adjust;
		newTime -= adjust;

		// Re-calculate milliseconds
		newTicks = 1000 * newTime / m_frequency;
	}

	// Record last time for adjust
	m_lastTime = newTime;

	// Test if it ever occurs that the timer returns a negative time.
	// When it does, find the cause and fix it.
	verify_range(newTicks, 0, std::numeric_limits<int64>::max());

	return static_cast<int64>(newTicks);
}
///////////////////////////////////////////////////////////////////////////////////////////////////